/*
 * Copyright (c) 2014-2015 Luca Agostini, Alessandro Cantini, Alessandro Dignani
 *
 * This file is part of MeteoCal.
 *
 * MeteoCal is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package it.polimi.se2.agostini_cantini_dignani.meteocal.business;

import it.polimi.se2.agostini_cantini_dignani.meteocal.entity.Event;
import it.polimi.se2.agostini_cantini_dignani.meteocal.entity.WeatherForecast;
import it.polimi.se2.agostini_cantini_dignani.meteocal.util.DateTimeUtils;

import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.ejb.Schedule;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * This singleton checks periodically the weather forecast of the events.
 *
 * @Author Alessandro Cantini
 */
@Startup
@Singleton
public class WeatherService {

	private static final int WEATHER_CHANGED_NOTIFICATION_FREQUENCY_IN_HOURS = 6;
	private static final int BAD_WEATHER_NOTIFICATION_SEND_HOUR = 7;
	private static List<Integer> DAYS_BEFORE_SENDING_BAD_WEATHER_NOTIFICATION = Arrays.asList(1, 3);

	@EJB
	private NotificationManager nm;

	@EJB
	private EventManager em;

	@EJB
	private WeatherForecastManager wfm;

	@PostConstruct
	private void init() {
		updateWeather();
	}

	/**
	 * Checks periodically every weather forecast related to events saved. If the forecast is
	 * different with respect to the previous one, it creates a new forecast related to the event
	 * and removes the older forecast, sending a notification to the user.
	 */
	@Schedule(hour = "*/" + WEATHER_CHANGED_NOTIFICATION_FREQUENCY_IN_HOURS, persistent = false)
	public void updateWeather() {
		WeatherForecast newForecast, oldForecast;
		for (Event e : em.findAll()) {
			oldForecast = e.getWeatherForecast();
			newForecast = wfm.getForecast(e.getPlace(), e.getStartTime(), false);

			if (newForecast != null && (oldForecast == null || !newForecast.hasSameWeather(oldForecast))) {
				e.setWeatherForecast(newForecast);
				em.editEvent(e, false);

				if (e.isOutdoor()) {
					nm.sendChangedWeatherNotification(e);
				}
			}
		}
	}

	/**
	 * Every day at 7 am the system sends a notification to the user if the weather is bad for
	 * events that start 1 or 3 days after.
	 */
	@Schedule(hour = "" + BAD_WEATHER_NOTIFICATION_SEND_HOUR, persistent = false)
	public void sendBadWeatherNotificationsThreeDaysBefore() {
		for (Event e : em.findAll()) {
			if (wfm.isBad(e.getWeatherForecast()) &&
					DAYS_BEFORE_SENDING_BAD_WEATHER_NOTIFICATION.contains(DateTimeUtils.distanceInDays(new Date(), e.getStartTime()))) {
				nm.sendBadWeatherNotification(e);
			}
		}
	}
}
